// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package collider

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"
)

const maxRoomCapacity = 2

// room 代表一个房间实例。
type room struct {
	parent *roomTable
	id     string
	// 从客户端 ID 到客户端对象的映射。
	clients         map[string]*client
	registerTimeout time.Duration
	roomSrvUrl      string
}

// newRoom 创建一个新的房间实例。
func newRoom(p *roomTable, id string, to time.Duration, rs string) *room {
	log.Printf(">>> 创建新房间: id=%s", id)
	return &room{parent: p, id: id, clients: make(map[string]*client), registerTimeout: to, roomSrvUrl: rs}
}

// client 返回指定的客户端，如果客户端不存在且房间未满，则创建它。
func (rm *room) client(clientID string) (*client, error) {
	if c, ok := rm.clients[clientID]; ok {
		log.Printf(">>> 在房间 %s 中找到现有客户端: %s", rm.id, clientID)
		return c, nil
	}
	if len(rm.clients) >= maxRoomCapacity {
		log.Printf("!!! 房间 %s 已满, 无法添加客户端 %s", rm.id, clientID)
		return nil, errors.New("房间已达到最大容量")
	}

	log.Printf(">>> 在房间 %s 中创建新客户端: %s", rm.id, clientID)
	var timer *time.Timer
	if rm.parent != nil {
		timer = time.AfterFunc(rm.registerTimeout, func() {
			if c := rm.clients[clientID]; c != nil {
				rm.parent.removeIfUnregistered(rm.id, c)
			}
		})
	}
	rm.clients[clientID] = newClient(clientID, timer)
	return rm.clients[clientID], nil
}

// register 将一个客户端绑定到 ReadWriteCloser。
func (rm *room) register(clientID string, rwc io.ReadWriteCloser) error {
	log.Printf(">>> 房间 %s 正在注册客户端 %s", rm.id, clientID)
	c, err := rm.client(clientID)
	if err != nil {
		log.Printf("!!! 房间 %s 获取客户端 %s 失败: %v", rm.id, clientID, err)
		return err
	}
	if err = c.register(rwc); err != nil {
		log.Printf("!!! 房间 %s 注册客户端 %s 失败: %v", rm.id, clientID, err)
		return err
	}

	log.Printf(">>> 客户端 %s 在房间 %s 中注册成功。当前房间人数: %d", clientID, rm.id, len(rm.clients))

	// 当房间满员（2人）时，安全地交换已排队的消息。
	if len(rm.clients) == 2 {
		log.Printf(">>> 房间 %s 已满员。开始交换排队的消息。", rm.id)

		var otherClient *client
		for _, clientInRoom := range rm.clients {
			if clientInRoom.id != clientID {
				otherClient = clientInRoom
				break
			}
		}

		if otherClient != nil {
			log.Printf(">>> 正在从 %s 发送排队消息到 %s", otherClient.id, c.id)
			if err := otherClient.sendQueued(c); err != nil {
				log.Printf("!!! 从 %s 发送排队消息到 %s 时出错: %v", otherClient.id, c.id, err)
			}

			log.Printf(">>> 正在从 %s 发送排队消息到 %s", c.id, otherClient.id)
			if err := c.sendQueued(otherClient); err != nil {
				log.Printf("!!! 从 %s 发送排队消息到 %s 时出错: %v", c.id, otherClient.id, err)
			}
		} else {
			log.Printf("!!! 严重错误: 房间 %s 人数为2，但找不到另一个客户端！", rm.id)
		}
	}
	return nil
}

// send 将消息发送到房间中的另一个客户端，如果另一个客户端尚未加入，则将消息排队。
func (rm *room) send(srcClientID string, msg string) error {
	src, err := rm.client(srcClientID)
	if err != nil {
		return err
	}

	// (关键修复) 对 loopback 呼叫中无效的 answer SDP 进行服务器端修复。
	// 我们在这里拦截从 LOOPBACK_CLIENT_ID 发来的、无效的 answer，并修正它。
	if srcClientID == "LOOPBACK_CLIENT_ID" {
		var tempMsg map[string]interface{}
		if err := json.Unmarshal([]byte(msg), &tempMsg); err == nil {
			if msgType, ok := tempMsg["type"].(string); ok && msgType == "answer" {
				if sdp, ok := tempMsg["sdp"].(string); ok && strings.Contains(sdp, "a=setup:actpass") {
					log.Println(">>> 检测到来自 LOOPBACK_CLIENT_ID 的无效 Answer，正在应用服务器端 SDP 补丁...")
					modifiedSdp := strings.Replace(sdp, "a=setup:actpass", "a=setup:active", 1)
					tempMsg["sdp"] = modifiedSdp
					if modifiedMsgBytes, err := json.Marshal(tempMsg); err == nil {
						msg = string(modifiedMsgBytes)
						log.Println(">>> SDP 补丁应用成功。")
					} else {
						log.Printf("!!! SDP 补丁: 重新编码 JSON 失败: %v", err)
					}
				}
			}
		}
	}

	// 如果另一个客户端尚未加入，则将消息排队。
	if len(rm.clients) == 1 {
		log.Printf(">>> 房间 %s 只有1人，消息将为客户端 %s 排队", rm.id, srcClientID)
		return rm.clients[srcClientID].enqueue(msg)
	}

	// 将消息发送到房间中的另一个客户端。
	for _, oc := range rm.clients {
		if oc.id != srcClientID {
			log.Printf(">>> 正在从 %s 发送消息到 %s", srcClientID, oc.id)
			return src.send(oc, msg)
		}
	}

	log.Printf("!!! 严重错误: 房间 %s 已损坏: %+v", rm.id, rm)
	return errors.New(fmt.Sprintf("损坏的房间 %+v", rm))
}

// remove 关闭客户端连接并移除由 |clientID| 指定的客户端。
func (rm *room) remove(clientID string) {
	if c, ok := rm.clients[clientID]; ok {
		c.deregister()
		delete(rm.clients, clientID)
		log.Printf(">>> 已从房间 %s 中移除客户端 %s", clientID, rm.id)

		// 向房间服务器发送 bye 消息。
		resp, err := http.Post(rm.roomSrvUrl+"/bye/"+rm.id+"/"+clientID, "text", nil)
		if err != nil {
			log.Printf("!!! 向房间服务器 %s 发送 BYE 失败: %v", rm.roomSrvUrl, err)
		}
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}
}

// empty 如果房间中没有客户端，则返回 true。
func (rm *room) empty() bool {
	return len(rm.clients) == 0
}

// wsCount 返回房间中已注册 WebSocket 的客户端数量。
func (rm *room) wsCount() int {
	count := 0
	for _, c := range rm.clients {
		if c.registered() {
			count += 1
		}
	}
	return count
}
